package org.dalmatian.common.log.aspect;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.dalmatian.common.core.domain.OperLogEvent;
import org.dalmatian.common.core.domain.model.LoginUser;
import org.dalmatian.common.core.utils.JsonUtils;
import org.dalmatian.common.core.utils.StringUtils;
import org.dalmatian.common.log.annotation.Log;
import org.dalmatian.common.log.enums.BusinessStatus;
import org.dalmatian.common.satoken.utils.LoginHelper;
import org.noear.solon.annotation.Component;
import org.noear.solon.core.event.EventBus;
import org.noear.solon.core.handle.*;
import org.noear.solon.core.wrap.MethodWrap;

/**
 * 操作日志记录处理
 *
 * @author Lion Li
 */
@Slf4j
//@Component
public class LogAspect implements Filter {


    /**
     * 计时 key
     */
    private static final ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();

    /**
     * 处理请求前执行
     */
    public void doBefore() {
        StopWatch stopWatch = new StopWatch();
        KEY_CACHE.set(stopWatch);
        stopWatch.start();
    }

    /**
     * 处理完请求后执行
     */
    public void doAfterReturning(Context ctx, Log controllerLog, Object jsonResult) {
        handleLog(ctx, controllerLog, null, jsonResult);
    }

    public void doAfterThrowing(Context ctx, Log controllerLog, Throwable e) {
        handleLog(ctx, controllerLog, e, null);
    }

    protected void handleLog(final Context ctx, Log controllerLog, final Throwable e, Object jsonResult) {
        try {

            // *========数据库日志=========*//
            OperLogEvent operLog = new OperLogEvent();
            operLog.setTenantId(LoginHelper.getTenantId());
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            // 请求的地址
            operLog.setOperIp(ctx.realIp());
            operLog.setOperUrl(StringUtils.substring(ctx.url(), 0, 255));
            LoginUser loginUser = LoginHelper.getLoginUser();
            operLog.setOperName(loginUser.getUsername());
            operLog.setDeptName(loginUser.getDeptName());

            if (e != null) {
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 3800));
            }
            MethodWrap method = ctx.action().method();
            // 设置方法名称
            operLog.setMethod(method.getDeclaringClz() + "." + method.getName() + "()");
            // 设置请求方式
            operLog.setRequestMethod(ctx.method());
            // 处理设置注解上的参数
            getControllerMethodDescription(ctx, controllerLog, operLog, jsonResult);
            // 设置消耗时间
            StopWatch stopWatch = KEY_CACHE.get();
            stopWatch.stop();
            operLog.setCostTime(ObjectUtil.isNull(stopWatch) ? 0 : stopWatch.getDuration().toMillis());
            // 发布事件保存数据库
            EventBus.publishAsync(operLog);
//            SpringUtils.context().publishEvent(operLog);
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("异常信息:{}", exp.getMessage());
        } finally {
            KEY_CACHE.remove();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log     日志
     * @param operLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(Context ctx, Log log, OperLogEvent operLog, Object jsonResult) throws Exception {
        // 设置action动作
        operLog.setBusinessType(log.businessType().ordinal());
        // 设置标题
        operLog.setTitle(log.title());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType().ordinal());
        // 是否需要保存request，参数和值
        if (log.isSaveRequestData()) {
            // 获取参数的信息，传入到数据库中。
            setRequestValue(ctx, operLog, log.excludeParamNames());
        }
        // 是否需要保存response，参数和值
        if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) {
            operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 3800));
        }
    }

    /**
     * 获取请求的参数，放到log中
     *
     * @param operLog 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue(Context ctx, OperLogEvent operLog, String[] excludeParamNames) throws Exception {
        operLog.setOperParam(StringUtils.substring(StrUtil.isNotBlank(ctx.body()) ? ctx.body() : ctx.paramMap().toString(), 0, 3800));
    }


    @Override
    public void doFilter(Context ctx, FilterChain chain) throws Throwable {
        doBefore();
        chain.doFilter(ctx);
        MethodWrap method = ctx.action().method();
        Log log = method.getAnnotation(Log.class);
        if (ObjectUtil.isNull(log)) {
            return;
        }

        System.out.println("#333");

        if (ObjectUtil.isNull(ctx.errors)) {
            doAfterReturning(ctx, log, ctx.result);
        } else {
            doAfterThrowing(ctx, log, ctx.errors);
        }
    }
}
